use std::fmt::Display;

type Error = &'static str;

#[derive(PartialEq)]
enum Type {
    Male,
    Female55,
    Female50,
}

impl TryFrom<&str> for Type {
    type Error = Error;
    fn try_from(value: &str) -> Result<Self, Self::Error> {
        match value {
            "男职工" => Ok(Type::Male),
            "原法定退休年龄55周岁女职工" => Ok(Type::Female55),
            "原法定退休年龄50周岁女职工" => Ok(Type::Female50),
            _ => Err("unknown type"),
        }
    }
}

#[derive(PartialEq, PartialOrd, Clone)]
struct YearMonth(u32);

impl TryFrom<&str> for YearMonth {
    type Error = Error;
    fn try_from(value: &str) -> Result<Self, Self::Error> {
        if let Some((y, m)) = value.split_once("-") {
            if let (Ok(year), Ok(month)) = (y.parse(), m.parse()) {
                return Ok(YearMonth::new(year, month));
            }
        }
        Err("YearMonth parse error")
    }
}

impl From<u32> for YearMonth {
    fn from(value: u32) -> Self {
        YearMonth(value)
    }
}

impl Display for YearMonth {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let (y, m) = self.get_year_month();
        f.write_fmt(format_args!("{}-{:02}", y, m))
    }
}

impl YearMonth {
    fn new(year: u16, month: u8) -> Self {
        YearMonth::from(year as u32 * 12 + (month - 1) as u32)
    }

    fn add_month(&mut self, month: u32) {
        self.0 += month
    }

    fn get_year_month(&self) -> (u16, u8) {
        ((self.0 / 12) as u16, (self.0 % 12) as u8 + 1)
    }

    fn diff(&self, from: &YearMonth) -> u32 {
        self.0 - from.0
    }
}

fn f32_format(value: f32) -> String {
    format!("{:.2}", value)
        .trim_end_matches('0')
        .trim_end_matches('.')
        .to_string()
}

fn retire_time_res(time: &str, tp: &str) -> Result<String, Error> {
    let birth = YearMonth::try_from(time)?;
    let start = YearMonth::new(2025, 1);
    let (retire_age, step, max_extra) = match Type::try_from(tp)? {
        Type::Male => (60, 4, 36),
        Type::Female55 => (55, 4, 36),
        Type::Female50 => (50, 2, 60),
    };
    let mut retire_year_month = birth.clone();
    retire_year_month.add_month(retire_age * 12);
    let mut extra_months = 0;

    if retire_year_month >= start {
        extra_months = (retire_year_month.diff(&start) / step) + 1;
        if extra_months > max_extra {
            extra_months = max_extra;
        }
    }
    retire_year_month.add_month(extra_months);
    let age = retire_year_month.diff(&birth) as f32 / 12f32;
    Ok(format!(
        "{},{},{}",
        retire_year_month,
        f32_format(age),
        extra_months
    ))
}

pub fn retire_time(time: &str, tp: &str) -> String {
    retire_time_res(time, tp).unwrap_or(String::new())
}
